// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/mac/call_with_eh_frame.h"

#include <stdint.h>
#include <unwind.h>

#include "build/build_config.h"

namespace base {
namespace mac {

#if defined(OS_IOS)
    // No iOS assembly implementation exists, so just call the block directly.
    void CallWithEHFrame(void (^block)(void))
    {
        block();
    }
#else // OS_MACOSX
    extern "C" _Unwind_Reason_Code __gxx_personality_v0(int,
        _Unwind_Action,
        uint64_t,
        struct _Unwind_Exception*,
        struct _Unwind_Context*);

    _Unwind_Reason_Code CxxPersonalityRoutine(
        int version,
        _Unwind_Action actions,
        uint64_t exception_class,
        struct _Unwind_Exception* exception_object,
        struct _Unwind_Context* context)
    {
        // Unwinding is a two-phase process: phase one searches for an exception
        // handler, and phase two performs cleanup. For phase one, this custom
        // personality will terminate the search. For phase two, this should delegate
        // back to the standard personality routine.

        if ((actions & _UA_SEARCH_PHASE) != 0) {
            // Tell libunwind that this is the end of the stack. When it encounters the
            // CallWithEHFrame, it will stop searching for an exception handler. The
            // result is that no exception handler has been found higher on the stack,
            // and any that are lower on the stack (e.g. in CFRunLoopRunSpecific), will
            // now be skipped. Since this is reporting the end of the stack, and no
            // exception handler will have been found, std::terminate() will be called.
            return _URC_END_OF_STACK;
        }

        return __gxx_personality_v0(version, actions, exception_class,
            exception_object, context);
    }
#endif // defined(OS_IOS)

} // namespace mac
} // namespace base
